Hook Modules
Overview
Hook modules (IHook) enable custom logic injection at specific points in the transaction lifecycle, allowing for pre- and post-execution checks and actions.
Interface Definition
interface IHook is IModule {
function preCheck(
address msgSender,
uint256 msgValue,
bytes calldata msgData
) external returns (bytes memory hookData);
function postCheck(bytes calldata hookData) external;
}
Implementation Example: Value Limiting Hook
Based on the provided MockHook implementation:
contract ValueLimitHook is IHook {
// State variables
mapping(address => uint256) public userMaxEthAmount;
mapping(address => bool) public isUserInitialized;
// Core Module Functions
function onInstall(bytes calldata data) external override {
uint256 maxAmount = abi.decode(data, (uint256));
userMaxEthAmount[msg.sender] = maxAmount;
isUserInitialized[msg.sender] = true;
}
function onUninstall(bytes calldata) external override {
isUserInitialized[msg.sender] = false;
delete userMaxEthAmount[msg.sender];
}
function isModuleType(uint256 moduleTypeId) external pure override returns (bool) {
return moduleTypeId == MODULE_TYPE_HOOK;
}
function isInitialized(address smartAccount) external view override returns (bool) {
return isUserInitialized[smartAccount];
}
// Hook-specific Functions
function preCheck(
address msgSender,
uint256 msgValue,
bytes calldata msgData
) external override returns (bytes memory hookData) {
require(
msgValue <= userMaxEthAmount[msgSender],
"Transfer value exceeds max amount"
);
return abi.encode(msgSender, msgValue, msgData);
}
function postCheck(bytes calldata hookData) external override {
// Post-execution validation if needed
(address sender, uint256 value, bytes memory data) = abi.decode(
hookData,
(address, uint256, bytes)
);
}
}
Extended Implementation: Activity Monitor Hook
contract ActivityMonitorHook is IHook {
// Track transaction counts and timing
mapping(address => uint256) public lastActivityTimestamp;
mapping(address => uint256) public dailyTransactionCount;
uint256 public constant MAX_DAILY_TRANSACTIONS = 10;
uint256 public constant DAY_SECONDS = 86400;
event ActivityRegistered(address indexed account, uint256 timestamp);
function preCheck(
address msgSender,
uint256 msgValue,
bytes calldata msgData
) external returns (bytes memory hookData) {
uint256 currentDay = block.timestamp / DAY_SECONDS;
uint256 lastDay = lastActivityTimestamp[msgSender] / DAY_SECONDS;
if (currentDay > lastDay) {
dailyTransactionCount[msgSender] = 0;
}
require(
dailyTransactionCount[msgSender] < MAX_DAILY_TRANSACTIONS,
"Daily transaction limit exceeded"
);
hookData = abi.encode(msgSender, block.timestamp);
return hookData;
}
function postCheck(bytes calldata hookData) external {
(address account, uint256 timestamp) = abi.decode(hookData, (address, uint256));
lastActivityTimestamp[account] = timestamp;
dailyTransactionCount[account]++;
emit ActivityRegistered(account, timestamp);
}
}
Common Use Cases
-
Transaction Monitoring
- Rate limiting
- Value limits
- Activity tracking
-
Security Checks
- Address validation
- Permission verification
- Risk assessment
-
State Management
- Balance tracking
- Counter management
- Status updates
Security Considerations
-
Pre-execution Safety
- Validate parameters
- Check conditions
- Handle edge cases
-
Post-execution Verification
- Verify state changes
- Update tracking
- Emit events
-
Data Handling
- Safe encoding/decoding
- Parameter validation
- State consistency
Best Practices
-
Hook Design
- Clear conditions
- Efficient checks
- Proper events
-
State Management
- Atomic updates
- Consistent state
- Safe operations
-
Error Handling
- Clear messages
- Proper reverts
- Parameter validation